using Godot;
using System;
using BrdgeBuider.Scripts.enums;
[GlobalClass]
public partial class PlatformCharacter : CharacterBody2D
{
	[Export] public string InitScene;
	[Export] public bool InputEnabled = true;
	[Export] public float Gravity=1000;
	[Export] public float JumpForce = 200f;
	[Export] public float CoyoteTime = 0.2f;
	[Export] public float JumpBufferTime = 0.2f;
	[Export] public int JumpCount = 2;
	[Export] public float MaxSpeed = 300.0f;
	[Export] public float MaxVerticalSpeed = 1000.0f;
	[Export] public float Acceleration = 3000.0f;
	[Export] public float Friction = 1000.0f;
	[Export] public float OnSpaceAccelerationRate = 0.2f;
	[Export] public float OnSpaceGravityRate = 0.5f;

	public void DealPause()
	{
		var ui = GetParent().GetNode<CanvasLayer>("PauseMenu");
		if (!ui.Visible)
		{
			ui.Show();
			InputEnabled = false;
		}
		else
		{
			ContinueGame();
		}
	}

	public void ContinueGame()
	{
		var ui = GetParent().GetNode<CanvasLayer>("PauseMenu");
		ui.Hide();
		InputEnabled = true;
	}

	public void ExitGame()
	{
		GetTree().Quit();
	}

	public void BackToStart()
	{
		GetTree().ChangeSceneToFile(InitScene);
	}

	private Vector2 GetPushFromInput(double delta)
	{
		if (!InputEnabled)
		{
			return new Vector2();
		}
		Vector2 direction = Input.GetVector("left", "right", "up", "down");
		direction *= Acceleration * (float)delta;
		return direction;
	}

	private Vector2 GetPush(double delta)
	{
		var direction = GetPushFromInput(delta);
		if (IsOnFloor() && direction.X == 0 && Velocity.X != 0)
		{
			direction.X = Friction * (float)delta * (Velocity.X > 0 ? -1 : 1);
			direction.X = Mathf.Clamp(direction.X, Mathf.Abs(Velocity.X) * -1, Mathf.Abs(Velocity.X));
		}
		if (!IsOnFloor())
		{
			direction.X *= OnSpaceAccelerationRate;
			if (JumpStatus==JumpStatus.Jumping)
			{
				direction.Y += Gravity * (float)delta*OnSpaceGravityRate;
			}
			else
			{
				direction.Y += Gravity * (float)delta;
			}
		}
		return direction;
	}

	private Vector2 ClampSpeed(Vector2 newVelocity)
	{
		newVelocity.X = Mathf.Clamp(newVelocity.X, -MaxSpeed, MaxSpeed);
		newVelocity.Y = Mathf.Clamp(newVelocity.Y, -MaxVerticalSpeed, MaxVerticalSpeed);
		return newVelocity;
	}

	public void SimulateMove(double delta)
	{
		var newVelocity = Velocity;
		newVelocity += GetPush(delta);
		if (UpdateJumpStatus())
		{
			newVelocity=Jump(newVelocity);
		}
		Velocity = ClampSpeed(newVelocity);
		MoveAndSlide();
	}

	private bool _isOnFloor=false;
	private double _lastTimeOnFloor = 0;
	private double _lastTimeRequestJump = 0;
	private int _curJumpCount = 0;
	public JumpStatus JumpStatus = JumpStatus.Landing;

	private bool UpdateJumpStatus()
	{
		var wasOnFloor = _isOnFloor;
		_isOnFloor = IsOnFloor();
		if (_isOnFloor)
		{
			_curJumpCount = 0;
		}

		if (wasOnFloor && !_isOnFloor)
		{
			_lastTimeOnFloor = (double)Time.GetTicksMsec() / 1000f;
		}

		if (InputEnabled&&Input.IsActionJustPressed("jump"))
		{
			_lastTimeRequestJump = (double)Time.GetTicksMsec() / 1000f;
		}

		double currentTime = (double)Time.GetTicksMsec() / 1000f;

		bool canJumpWithCoyoteTime = _isOnFloor|| currentTime - _lastTimeOnFloor <= CoyoteTime;
		bool requestJumpWithBuffer = (currentTime - _lastTimeRequestJump) <= JumpBufferTime;
		bool hasJumpCount = _curJumpCount < JumpCount;
		JumpStatus newStat = JumpStatus;
		if (JumpStatus==JumpStatus.Land)
		{
			if (canJumpWithCoyoteTime&&requestJumpWithBuffer&&hasJumpCount)
			{
				newStat = JumpStatus.Jumping;
			}else if (!canJumpWithCoyoteTime&&Velocity.Y<0)
			{
				newStat = JumpStatus.Upping;
			}else if (!canJumpWithCoyoteTime&&Velocity.Y>0)
			{
				newStat = JumpStatus.Landing;
			}
		}else if (JumpStatus==JumpStatus.Jumping)
		{
			if (_isOnFloor)
			{
				newStat = JumpStatus.Land;
			}else if (InputEnabled&&Input.IsActionJustReleased("jump")&&Velocity.Y<0)
			{
				newStat = JumpStatus.Upping;
			}else if (Velocity.Y>0)
			{
				newStat = JumpStatus.Landing;
			}
		}else if (JumpStatus==JumpStatus.Upping)
		{
			if (_isOnFloor)
			{
				newStat = JumpStatus.Land;
			}else if (hasJumpCount&&requestJumpWithBuffer)
			{
				newStat = JumpStatus.Jumping;
			}else if (Velocity.Y>0)
			{
				newStat = JumpStatus.Landing;
			}
		}else if (JumpStatus==JumpStatus.Landing)
		{
			if (_isOnFloor)
			{
				newStat = JumpStatus.Land;
			}else if (Velocity.Y<0)
			{
				newStat = JumpStatus.Upping;
			}else if (requestJumpWithBuffer&&hasJumpCount)
			{
				newStat = JumpStatus.Jumping;
			}
		}
		bool triggerJump = newStat != JumpStatus && newStat == JumpStatus.Jumping;
		if (JumpStatus!=newStat)
		{
			// GD.PrintS(_jumpStatus,newStat);
			if (JumpStatus==JumpStatus.Land&&newStat!=JumpStatus.Jumping)
			{
				_curJumpCount++;
			}
		}
		JumpStatus = newStat;
		return triggerJump;
	}

	private Vector2 Jump(Vector2 newVelocity)
	{
		_lastTimeRequestJump = -1000;
		newVelocity.Y = Mathf.Min(-JumpForce,newVelocity.Y-JumpForce);
		_curJumpCount++;
		return newVelocity;
	}

	public override void _PhysicsProcess(double delta)
	{
		SimulateMove(delta);
		if (Position.Y>648)
		{
			SetPosition(new Vector2(232f, 100f));
		}

		if (Input.IsActionJustPressed("exit"))
		{
			DealPause();
		}
	}
}
